package triangulation;

import java.util.ArrayList;

import sample.ArtGallery;
import sample.Edge;
import sample.Vertex;

/*
 * Detects intersection between two edge.
 * Use Intersection.areEdgesIntersect(Edge e1, Edge e2) static function to
 * find whether there is an intersection or not.
 */
public class Intersection
{
	private static final double EPSILON = 0.000001;

	public static boolean areEdgesIntersect(Edge e1, Edge e2)
	{

		// System.out.println("isBoundingBoxesIntersects = "+areBoundingBoxesIntersected(e1,e2));
		// System.out.println("lineSegmentTouchesOrCrossesEdge = "+lineSegmentCrossesEdge(e1,
		// e2));
		// System.out.println("lineSegmentTouchesOrCrossesEdge ="+lineSegmentCrossesEdge(e2,
		// e1));

//		int e1index1 = e1.getSource().getIndex();
//		int e1index2 = e1.getTarget().getIndex();
//		int e2index1 = e2.getSource().getIndex();
//		int e2index2 = e2.getTarget().getIndex();
//
//		if (e1index1 == e2index1 || e1index1 == e2index2 || e1index2 == e2index1 || e1index2 == e2index2)
//		{
//			return false;
//		}
		
		if((e1.getSource().equals(e2.getSource()) || e1.getTarget().equals(e2.getTarget())) ||
				(e1.getSource().equals(e2.getTarget()) || e1.getTarget().equals(e2.getSource()))){
			return false;
		}

		return areBoundingBoxesIntersected(e1, e2) && lineSegmentCrossesEdge(e1, e2) && lineSegmentCrossesEdge(e2, e1);
	}

	private static boolean areBoundingBoxesIntersected(Edge e1, Edge e2)
	{
		ArrayList<Vertex> rectE1 = boundingBoxVertices(e1);
		ArrayList<Vertex> rectE2 = boundingBoxVertices(e2);

		return rectE1.get(0).getX() < rectE2.get(1).getX() && rectE1.get(1).getX() > rectE2.get(0).getX()
				&& rectE1.get(0).getY() < rectE2.get(1).getY() && rectE1.get(1).getY() > rectE2.get(0).getY();
	}

	private static ArrayList<Vertex> boundingBoxVertices(Edge e)
	{
		ArrayList<Vertex> rect = new ArrayList<Vertex>();

		int minX = (int) Math.min(e.getSource().getX(), e.getTarget().getX());
		int maxX = (int) Math.max(e.getSource().getX(), e.getTarget().getX());
		int minY = (int) Math.min(e.getSource().getY(), e.getTarget().getY());
		int maxY = (int) Math.max(e.getSource().getY(), e.getTarget().getY());

		rect.add(new Vertex(minX, minY));
		rect.add(new Vertex(maxX, maxY));

		return rect;
	}

	private static boolean isVertexOnEdge(Vertex v, Edge e)
	{
		Edge tmpEdge = new Edge(new Vertex(0, 0), new Vertex(e.getTarget().getX() - e.getSource().getX(), e.getTarget()
				.getY() - e.getSource().getY()));

		Vertex tmpVertex = new Vertex(v.getX() - e.getSource().getX(), v.getY() - e.getSource().getY());

		double r = crossProduct(tmpEdge.getTarget(), tmpVertex);

		return Math.abs(r) < EPSILON;
	}

	private static boolean isVertexRightOfEdge(Vertex v, Edge e)
	{
		Edge tmpEdge = new Edge(new Vertex(0, 0), new Vertex(e.getTarget().getX() - e.getSource().getX(), e.getTarget()
				.getY() - e.getSource().getY()));

		Vertex tmpVertex = new Vertex(v.getX() - e.getSource().getX(), v.getY() - e.getSource().getY());
		return crossProduct(tmpEdge.getTarget(), tmpVertex) < 0;
	}

	private static boolean lineSegmentCrossesEdge(Edge e1, Edge e2)
	{
		return isVertexOnEdge(e2.getSource(), e1) || isVertexOnEdge(e2.getTarget(), e1)
				|| (isVertexRightOfEdge(e2.getSource(), e1) ^ isVertexRightOfEdge(e2.getTarget(), e1));
		// return (isVertexRightOfEdge(e2.getSource(), e1) ^
		// isVertexRightOfEdge(e2.getTarget(), e1));
	}

	/**
	 * While using this intersection method, give first parameter as the bisector's line and the second one as the desired edge.
	 * 
	 * @param r1
	 * @param e
	 * @return intersection point (x,y) if it exists. return null otherwise.
	 */
	public static Vertex findIntersectionOfLines(double[] r1, Edge e, ArtGallery ag)
	{
		Vertex intersectionPoint = new Vertex();
		boolean isInRange = false;

		double[] r2 = e.calculateLineEquation();

		// calculate the intersection point.
		intersectionPoint.setX((int) Math.round((r2[1] - r1[1]) / (r1[0] - r2[0]))); // x
		intersectionPoint.setY((int) Math.round(intersectionPoint.getX() * r1[0] + r1[1])); // y

		if (e.getSource().getX() <= e.getTarget().getX() && e.getSource().getX() <= intersectionPoint.getX()
				&& intersectionPoint.getX() <= e.getTarget().getX())
		{
			isInRange = true;
			// System.out.println("intersection: (" + intersectionPoint.getX() + ", "
			// + intersectionPoint.getY());
		}
		else if (e.getTarget().getX() <= e.getSource().getX() && e.getTarget().getX() <= intersectionPoint.getX()
				&& intersectionPoint.getX() <= e.getSource().getX())
		{
			isInRange = true;
			// System.out.println("intersection: (" + intersectionPoint.getX() + ", "
			// + intersectionPoint.getY());
		}
		// else
		// {
		// System.out.println("DEBUG");
		// System.out.println(e.getSource());
		// System.out.println(e.getTarget());
		// System.out.println(intersectionPoint);
		// System.out.println("no intersection");
		// }

		if (isInRange && !e.getSource().equals(intersectionPoint) && !e.getTarget().equals(intersectionPoint))
		{
			ag.guardEdge = e;
			return intersectionPoint;
		}
		else
			return null;
	}

	/**
	 * Given two edges, find the line of their bisector(y = mx+c). return slope of the bisector as the first element of the array and then
	 * return the constant as the second.
	 * 
	 * @param e1
	 * @param e2
	 * @return bisector line [0]:slope, [1]:constant
	 */
	public static double[] findBisectorLine(Edge e1, Edge e2)
	{
		Edge connector = new Edge();
		Edge shortEdge, longEdge;
		Vertex temp = new Vertex();
		Vertex common = e1.getCommonVertex(e2);

		double lengthE1 = e1.getLength();
		double lengthE2 = e2.getLength();
		double minLength = Math.min(lengthE1, lengthE2);

		double alpha1 = minLength / lengthE1;
		double alpha2 = minLength / lengthE2;
		double alphaValue;

		if (lengthE1 < lengthE2)
		{
			shortEdge = e1;
			longEdge = e2;
			alphaValue = alpha2;
		}
		else
		{
			shortEdge = e2;
			longEdge = e1;
			alphaValue = alpha1;
		}

		if (longEdge.getSource().equals(common))
		{
			temp.setX((int) (alphaValue * longEdge.getTarget().getX() + (1 - alphaValue) * common.getX()));
			temp.setY((int) (alphaValue * longEdge.getTarget().getY() + (1 - alphaValue) * common.getY()));
		}
		else if (longEdge.getTarget().equals(common))
		{
			temp.setX((int) (alphaValue * longEdge.getSource().getX() + (1 - alphaValue) * common.getX()));
			temp.setY((int) (alphaValue * longEdge.getSource().getY() + (1 - alphaValue) * common.getY()));
		}

		connector.setSource(shortEdge.getOtherEnd(common));
		connector.setTarget(temp);

		Edge bisectorEdge = new Edge(common, new Vertex(
				(connector.getSource().getX() + connector.getTarget().getX()) / 2,
				(connector.getSource().getY() + connector.getTarget().getY()) / 2));

		double eqn[] = bisectorEdge.calculateLineEquation();

		return eqn;
	}

	private static double crossProduct(Vertex v1, Vertex v2)
	{
		return v1.getX() * v2.getY() - v2.getX() * v1.getY();
	}
}
